package com.castle.fortress.admin.flowable.form.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.castle.fortress.admin.flowable.enums.ApproveStatusEnum;
import com.castle.fortress.admin.flowable.form.entity.FlowableFormConfigEntity;
import com.castle.fortress.admin.flowable.form.service.FlowableFormConfigService;
import com.castle.fortress.admin.flowable.form.service.FlowableFormDataService;
import com.castle.fortress.admin.form.dto.ColConditionDto;
import com.castle.fortress.admin.form.dto.QueryConditionDto;
import com.castle.fortress.admin.form.enums.DbQueryEnum;
import com.castle.fortress.admin.form.mapper.FormDataMapper;
import com.castle.fortress.admin.form.service.FormModelService;
import com.castle.fortress.admin.message.sms.dto.ConfigSmsDto;
import com.castle.fortress.admin.message.sms.service.ConfigSmsService;
import com.castle.fortress.admin.message.sms.service.SmsService;
import com.castle.fortress.admin.oa.dto.HolidayTypeDto;
import com.castle.fortress.admin.oa.entity.UserHolidaySettingEntity;
import com.castle.fortress.admin.oa.service.HolidayTypeService;
import com.castle.fortress.admin.oa.service.UserHolidaySettingService;
import com.castle.fortress.admin.system.entity.SysUser;
import com.castle.fortress.admin.utils.BizCommonUtil;
import com.castle.fortress.admin.utils.RedisUtils;
import com.castle.fortress.admin.utils.WebUtil;
import com.castle.fortress.common.entity.RespBody;
import com.castle.fortress.common.enums.SmsPlatFormEnum;
import com.castle.fortress.common.enums.YesNoEnum;
import com.castle.fortress.common.exception.BizException;
import com.castle.fortress.common.respcode.BizErrorCode;
import com.castle.fortress.common.respcode.GlobalRespCode;
import com.castle.fortress.common.utils.CommonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 表数据管理 服务实现类
 *
 * @author castle
 * @since 2021-11-10
 */
@Service
public class FlowableFormDataServiceImpl implements FlowableFormDataService {
    @Autowired
    private FormDataMapper formDataMapper;
    @Autowired
    private FormModelService formModelService;
    @Autowired
    private FlowableFormConfigService flowableFormConfigService;
    @Autowired
    private ConfigSmsService configSmsService;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private SmsService smsService;
    @Autowired
    private HolidayTypeService holidayTypeService;
    @Autowired
    private UserHolidaySettingService userHolidaySettingService;
    //验证码模板
    @Value("${castle.captcha.formCode}")
    private String smsCode;


    @Override
    public boolean saveData(String tbId,String tbName, Map<String, Object> dataMap) {
        boolean result=false;
        if(dataMap==null||dataMap.isEmpty()){
            throw new BizException(GlobalRespCode.PARAM_MISSED);
        }
        //判断新增还是修改
        boolean updateFlag = (dataMap.get("id")==null||"undefined".equals(dataMap.get("id").toString()))?false:true;
        //校验验证码
        boolean captchaFlag = false,captchaExist=false;
        for(String key:dataMap.keySet()){
            if("phone_sms".equals(key)) {
                captchaExist=true;
                //新增时校验小程序，修改不校验
                if(!updateFlag){
                    if(dataMap.get("code_sms")==null  || StrUtil.isEmpty(dataMap.get("code_sms").toString())){
                        throw new BizException(GlobalRespCode.PARAM_MISSED,"请输入验证码");
                    }
                    captchaFlag=true;
                }
            }
        }
        //校验验证码
        if(captchaFlag ){
            checkCode(tbId,dataMap.get("phone_sms").toString(),dataMap.get("code_sms").toString());
        }
        if(captchaExist){
            dataMap.remove("code_sms");
        }
        //校验指定字段是否重复
        String repeatResult = checkRepeat(tbId,dataMap);
        if(!"success".equals(repeatResult)){
            throw new BizException(BizErrorCode.FORM_COL_REPEAT_ERROR,repeatResult);
        }
        List<String> colName=new ArrayList<>();
        List<Object> colValue=new ArrayList<>();
        List<Map<String,Object>> updateList=new ArrayList<>();
        String id=null;
        for(String key:dataMap.keySet()){
            if(key.toLowerCase(Locale.ROOT).equals("id")) {
                if (StrUtil.isNotEmpty(dataMap.get(key).toString()) && !dataMap.get(key).toString().toLowerCase(Locale.ROOT).equals("undefined") && !dataMap.get(key).toString().toLowerCase(Locale.ROOT).equals("null")) {
                    id = dataMap.get(key).toString();
                }
            //创建人
            }else if("create_user".equals(key)){
                colName.add("create_user");
                SysUser user = WebUtil.currentUser();
                if(user==null){
                    throw new BizException(GlobalRespCode.NO_LOGIN_ERROR);
                }
                colValue.add(user.getId());
                //创建人
            }else if("create_time".equals(key)){
                colName.add("create_time");
                colValue.add(new Date());
            }else if(dataMap.get(key)!=null&& StrUtil.isNotEmpty(dataMap.get(key).toString())){


                Map<String,Object> m=new HashMap<>();
                if(dataMap.get(key) instanceof List) {
                    List l=(List) dataMap.get(key);
                    if(l.size()>0 && l.get(0) instanceof  Map){
                        colValue.add(new JSONArray((List) dataMap.get(key)).toString());
                        m.put("value", new JSONArray((List) dataMap.get(key)).toString());
                    }else{
                        colValue.add(CollectionUtil.join((List) dataMap.get(key), ","));
                        m.put("value", CollectionUtil.join((List) dataMap.get(key), ","));
                    }
                    colName.add(key);
                    m.put("name",key);
                }
                else if(dataMap.get(key) instanceof Map){
                    // 判断key 是不是请假
                    JSONObject value = new JSONObject(dataMap.get(key));
                    colValue.add(value.toString());
                    m.put("value", new JSONObject(dataMap.get(key)).toString());
                    colName.add(key);
                    m.put("name",key);
                    if ("fortress_holiday".equals(key)){
                        //请假类型
                        String type = (String) value.get("type");
                        colName.add("_castle_leave_type");
                        colValue.add(type);
                        m.put("name","_castle_leave_type");
                        m.put("value", type);
                        //请假开始时间
                        colName.add("_castle_start_time");
                        colValue.add(value.get("startTime"));
                        m.put("name","_castle_start_time");
                        m.put("value", value.get("startTime"));
                        //请假开始时间 (上午下午)
                        colName.add("_castle_start_type");
                        colValue.add(value.get("startType"));
                        m.put("name","_castle_start_type");
                        m.put("value", value.get("startType"));
                        //请假结束时间
                        colName.add("_castle_end_time");
                        colValue.add(value.get("endTime"));
                        m.put("name","_castle_end_time");
                        m.put("value", value.get("endTime"));
                        //请假结束时间 (上午下午)
                        colName.add("_castle_end_type");
                        colValue.add(value.get("endType"));
                        m.put("name","_castle_end_type");
                        m.put("value", value.get("endType"));
                        //请假时长
                        colName.add("_castle_leave_duration");
                        String leaveDuration = String.valueOf(value.get("leaveDuration"));
                        colValue.add(leaveDuration);
                        m.put("name","_castle_leave_duration");
                        m.put("value", leaveDuration);
                        // 请假类型不为空.  并且 请假天数不为空
                        if(StrUtil.isNotEmpty(type) && StrUtil.isNotEmpty(leaveDuration)){
                            final HolidayTypeDto holidayTypeDto = holidayTypeService.getByIdExtends(Long.parseLong(type));
                            // 有限制时长的请假类型
                            if (YesNoEnum.YES.getCode().equals(holidayTypeDto.getIsLimit())) {
                                SysUser user = WebUtil.currentUser();
                                //限制时长
                                // 计算 剩余假期是否足够
                                // 不足 异常 假期余额不足
                                // 足够 扣减假期余额
                                // 获取当前用户该请假类型的剩余天数
                                final QueryWrapper<UserHolidaySettingEntity> queryWrapper = new QueryWrapper<>();
                                queryWrapper.eq("user_id", user.getId());
                                queryWrapper.eq("holiday_type_id", type);
                                queryWrapper.last("limit 1");
                                final UserHolidaySettingEntity one = userHolidaySettingService.getOne(queryWrapper);
                                // 请假的天数
                                BigDecimal holiday_total_days = new BigDecimal(leaveDuration);
                                // 剩余的天数
                                BigDecimal surplusAmount = one.getSurplusAmount();
                                // 如果剩余天数不足
                                if (surplusAmount.compareTo(holiday_total_days)== -1) {
                                    throw new BizException("剩余假期额度不足");
                                } else {
                                    //计算剩余数量并更新
                                    one.setSurplusAmount((surplusAmount.subtract(holiday_total_days)));
                                    userHolidaySettingService.updateById(one);
                                }
                            }
                        }
                    }
                }
                else{
                    colValue.add(dataMap.get(key));
                    m.put("value",dataMap.get(key));
                    colName.add(key);
                    m.put("name",key);
                }

                updateList.add(m);

            }
        }
        if(!colName.isEmpty()){
            if(StrUtil.isNotEmpty(id)){
                formDataMapper.updateData(tbName,id,updateList);
                result=true;
            }else{
                //初始化id
                long initId = IdWorker.getId();
                colName.add("id");
                colValue.add(initId);
                dataMap.put("id",initId);
                formDataMapper.saveData(tbName,colName,colValue);
                result=true;
            }
        }
        return result;
    }

    @Override
    public boolean updateApproveStatus(String tbId, String id, String approveStatus) {
        FlowableFormConfigEntity configEntity = flowableFormConfigService.selByIdIgnoreDel(tbId);
        //校验表名是否存在
        String tbName = "castle_approve_form"+tbId;
        if(configEntity==null || !formModelService.isExistTable(tbName)){
            throw new BizException(GlobalRespCode.UNAUTHORIZED);
        }
        List<Map<String,Object>> updateList=new ArrayList<>();
        Map<String,Object> approveStatusMap = new HashMap<>();
        approveStatusMap.put("name","_castle_approve_status");
        approveStatusMap.put("value",approveStatus);
        updateList.add(approveStatusMap);
        //审批时间
        if(ApproveStatusEnum.FINISH.getCode().toString().equals(approveStatus)||ApproveStatusEnum.UNAPPROVED.getCode().toString().equals(approveStatus)||ApproveStatusEnum.CANCEL.getCode().toString().equals(approveStatus)){
            Map<String,Object> approveDateMap = new HashMap<>();
            approveDateMap.put("name","_castle_approve_date");
            approveDateMap.put("value",new Date());
            updateList.add(approveDateMap);
        }
        return formDataMapper.updateData(tbName,id,updateList);
    }

    @Override
    public void updateProcessId(String tbId, String id, String processId,String engineVersion) {
        FlowableFormConfigEntity configEntity = flowableFormConfigService.getById(tbId);
        //校验表名是否存在
        String tbName = "castle_approve_form"+tbId;
        if(configEntity==null || !formModelService.isExistTable(tbName)){
            throw new BizException(GlobalRespCode.UNAUTHORIZED);
        }
        List<Map<String,Object>> updateList=new ArrayList<>();
        Map<String,Object> processIdMap = new HashMap<>();
        processIdMap.put("name","_castle_process_instance_id");
        processIdMap.put("value",processId);
        updateList.add(processIdMap);
        Map<String,Object> processEngineVersionMap = new HashMap<>();
        processEngineVersionMap.put("name","_castle_process_engine_version");
        processEngineVersionMap.put("value",engineVersion);
        updateList.add(processEngineVersionMap);
        formDataMapper.updateData(tbName,id,updateList);
    }

    @Override
    public IPage<Map<String, Object>> queryPageData(QueryConditionDto queryConditionDto) {
        queryConditionDto.setPageFlag(true);//分页显示
        queryConditionDto.setOffset(BizCommonUtil.getOffset(queryConditionDto.getCurrent(),queryConditionDto.getSize()).get("offset"));
        List<Map<String, Object>> dataList=queryListData(queryConditionDto);
        Long total = queryCount(queryConditionDto);
        Page<Map<String, Object>> pageDto = new Page(queryConditionDto.getCurrent(), queryConditionDto.getSize(),total);
        pageDto.setRecords(dataList);
        return pageDto;
    }

    @Override
    public List<Map<String, Object>> queryListData(QueryConditionDto queryConditionDto) {
        FlowableFormConfigEntity configEntity = flowableFormConfigService.getById(queryConditionDto.getTbId());
        //校验表名是否存在
        if(configEntity==null || !formModelService.isExistTable("castle_approve_form"+queryConditionDto.getTbId())){
            throw new BizException(GlobalRespCode.UNAUTHORIZED);
        }
        if(configEntity.getDesignContent().indexOf("create_user")!=-1){
            queryConditionDto.setUserFlag(true);
        }
        if(configEntity.getDesignContent().indexOf("create_time")!=-1){
            queryConditionDto.setOrderFlag(true);
        }
        queryConditionDto.setTbId("castle_approve_form"+queryConditionDto.getTbId());
        //校验查询条件 参数合法且不为空
        queryConditionDto.setConditionDtoList(initConditions(configEntity.getQueryCondition(),queryConditionDto.getConditionJson()));
        List<Map<String, Object>> data = formDataMapper.queryData(queryConditionDto);
        //时间格式化
        if(queryConditionDto.getOrderFlag()){
            for(Map<String, Object> m:data){
                if(m.get("create_time")!=null){
                    m.put("create_time", DateUtil.format((Date) m.get("create_time"), "yyyy-MM-dd HH:mm:ss"));
                }
            }
        }
        return data;
    }

    @Override
    public Long queryCount(QueryConditionDto queryConditionDto) {
        //校验查询条件 参数合法且不为空
        return formDataMapper.queryCount(queryConditionDto);
    }

    /**
     * 校验查询条件并初始化 参数合法且不为空
     * @param conditionConfig 条件配置
     * @param conditionJson 条件数据
     * @return
     */
    private List<ColConditionDto> initConditions(String conditionConfig,String conditionJson){
        List<ColConditionDto> conditions= new ArrayList<>();
        if(StrUtil.isEmpty(conditionConfig)||StrUtil.isEmpty(conditionJson)){
            return null;
        }
        try {
            //校验查询条件 参数合法且不为空
            conditions = new ArrayList<>();
            JSONArray configArray=new JSONArray(conditionConfig);
            if(StrUtil.isNotEmpty(conditionJson)){
                JSONObject obj=new JSONObject(conditionJson);
                for(String key:obj.keySet()){
                    if(obj.get(key)!=null&&StrUtil.isNotEmpty(obj.get(key).toString())){
                        ColConditionDto colConditionDto=new ColConditionDto();
                        colConditionDto.setColName(key);
                        for(int i=0;i<configArray.size();i++){
                            JSONObject o=configArray.getJSONObject(i);
                            if(o.getStr("model").equals(key)){
                                if(o.getBool("checked")&&StrUtil.isNotEmpty(o.getStr("queryType"))){
                                    colConditionDto.setConditionCode(o.getInt("queryType"));
                                    String conditionName = DbQueryEnum.getNameByCode(colConditionDto.getConditionCode());
                                    //校验查询方法是否支持
                                    if(StrUtil.isNotEmpty(conditionName)){
                                        colConditionDto.setConditionName(conditionName);
                                        String[] vs=obj.getStr(key).split("@;@");
                                        //校验查询字段值是否有效
                                        if(vs.length==0||StrUtil.isEmpty(vs[0])){
                                            break;
                                        }
                                        List<String> values=new ArrayList<>();
                                        //特殊处理的查询方法 like
                                        if(DbQueryEnum.LIKE.getCode().equals(colConditionDto.getConditionCode())) {
                                            values.add("%" + vs[0] + "%");
                                        }else if(DbQueryEnum.BETWEEN.getCode().equals(colConditionDto.getConditionCode())){
                                            values.add(vs[0]);
                                            values.add(vs[1]);
                                        }else{
                                            values.add(vs[0]);
                                        }
                                        colConditionDto.setValues(values.toArray());
                                        conditions.add(colConditionDto);
                                    }
                                }
                                break;
                            }
                        }
                    }
                }
            }
        } catch (JSONException e) {
            return null;
        }
        return conditions.isEmpty()?null:conditions;
    }

    @Override
    public Map<String, Object> queryById(String tbId, String id) {
        //校验表名是否存在
        if(!formModelService.isExistTable("castle_approve_form"+tbId)){
            throw new BizException(GlobalRespCode.UNAUTHORIZED);
        }
        Map<String, Object> data = formDataMapper.queryById("castle_approve_form"+tbId,id);
        //处理数组类型的组件
        FlowableFormConfigEntity entity = flowableFormConfigService.selByIdIgnoreDel(tbId);
        JSONArray design=new JSONArray(entity.getDesignContent());
        for(int j=0;j<design.size();j++){
            JSONObject d=design.getJSONObject(j);
            String tag = d.getJSONObject("__config__").getStr("tag");
            String tagType= d.containsKey("type")?d.getStr("type"):"";
            boolean tagIsRange= d.containsKey("is-range")?d.getBool("is-range"):false;
            String vModel = d.getStr("__vModel__");
            if(tag.equals("el-checkbox-group")
            ||(tag.equals("el-time-picker")&&tagIsRange)
            ||(tag.equals("el-date-picker")&&(tagType.indexOf("range")>0))
            ||tag.equals("el-cascader")
            ||(tag.equals("el-select")&&d.getBool("multiple"))
            ||(tag.equals("image-upload")&&d.getBool("multiple"))){
                if(data.get(vModel)!=null&&StrUtil.isNotEmpty(data.get(vModel).toString())){
                    data.put(vModel,data.get(vModel).toString().split(","));
                }else{
                    data.put(vModel,new ArrayList<Object>());
                }
            //文件特殊处理
            }else if(tag.equals("file-upload")){
                if(data.get(vModel)!=null&&StrUtil.isNotEmpty(data.get(vModel).toString())){
                    //多文件
                    if(data.get(vModel).toString().startsWith("[")){
                        data.put(vModel,new JSONArray(data.get(vModel).toString()));
                    //单文件
                    }else{
                        data.put(vModel,new JSONObject(data.get(vModel).toString()));
                    }
                }
            }
        }
        return data;
    }

    @Override
    public boolean removeByIds(String tbId, List<Long> ids) {
        //校验表名是否存在
        if(!formModelService.isExistTable("castle_approve_form"+tbId)){
            throw new BizException(GlobalRespCode.UNAUTHORIZED);
        }
        return formDataMapper.removeByIds("castle_approve_form"+tbId,ids);
    }

    @Override
    public boolean getCode(Map<String, Object> map) {
        if(CommonUtil.verifyParamNull(map,map.get("tbId"),map.get("phone_sms"))){
            throw new BizException(GlobalRespCode.PARAM_MISSED);
        }
        String tbId = map.get("tbId").toString();
        String mobile = map.get("phone_sms").toString();
        //校验表名是否存在
        if(!formModelService.isExistTable("castle_approve_form"+tbId)){
            throw new BizException(GlobalRespCode.UNAUTHORIZED);
        }
        //获取短信模板
        ConfigSmsDto configSmsDto = new ConfigSmsDto();
        configSmsDto.setSmsCode(smsCode);
        configSmsDto.setStatus(YesNoEnum.YES.getCode());
        List<ConfigSmsDto> templates = configSmsService.listConfigSms(configSmsDto);
        if(templates==null || templates.size()!=1){
            throw new BizException(GlobalRespCode.DB_DATA_ERROR);
        }
        ConfigSmsDto template = templates.get(0);
        // 6位随机验证码
        String code = String.valueOf((int)(Math.random() * 1000000));
        String params = "";
        //阿里云模板 只支持 code参数
        if(SmsPlatFormEnum.ALIYUN.getCode().equals(template.getPlatform())){
            params="{\"code\":"+code+"}";
        }else if(SmsPlatFormEnum.TENCENTCLOUD.getCode().equals(template.getPlatform())){
            params=code;
        }else{
            throw new BizException(GlobalRespCode.DB_DATA_ERROR);
        }
        //获取配置有效期
        long expireTime = 5;
        FlowableFormConfigEntity configEntity = flowableFormConfigService.getById(tbId);
        JSONArray design=new JSONArray(configEntity.getDesignContent());
        for(int j=0;j<design.size();j++){
            JSONObject d=design.getJSONObject(j);
            String tag = d.getJSONObject("__config__").getStr("tag");
            String vModel = d.getStr("__vModel__");
            if(tag.equals("phone_sms")){
                expireTime = d.getJSONObject("__config__").getLong("validityMinute");
            }
        }
        String key = tbId+"_"+mobile;
        RespBody rb = smsService.send(smsCode,mobile,params);
        if(rb.isSuccess()){
            redisUtils.set(key,code,expireTime, TimeUnit.MINUTES);
            return true;
        }else{
            throw new BizException(rb.getMsg());
        }
    }

    @Override
    public boolean checkCode(String tbId,String mobile,String code) {
        String key = tbId+"_"+mobile;
        String value = redisUtils.get(key)==null?null:redisUtils.get(key).toString();
        if(value == null){
            throw new BizException(GlobalRespCode.CAPTCHA_EXPIRED_ERROR);
        }else if(!value.equals(code)){
            throw new BizException(GlobalRespCode.CAPTCHA_CODE_VERIFY_ERROR);
        }else{
            redisUtils.remove(key);
        }
        return true;
    }

    @Override
    public String checkRepeat(String tbId, Map<String, Object> dataMap) {
        //判断新增还是修改
        boolean updateFlag = (dataMap.get("id")==null||"undefined".equals(dataMap.get("id").toString()))?false:true;
        //获取唯一校验的字段
        Map<String,String> keys = new HashMap<>();
        FlowableFormConfigEntity configEntity = flowableFormConfigService.getById(tbId);
        JSONArray design=new JSONArray(configEntity.getDesignContent());
        for(int j=0;j<design.size();j++){
            JSONObject d=design.getJSONObject(j);
            JSONObject config = d.getJSONObject("__config__");
            String vModel = d.getStr("__vModel__");
            if(config.get("isUnique")!=null && config.getBool("isUnique")){
                keys.put(vModel,config.getStr("label"));
            }
        }
        for(String uniqueKey:keys.keySet()){
            if(dataMap.get(uniqueKey)!=null){
                List<Long> ids = formDataMapper.queryIdsRepeat("castle_approve_form"+tbId,uniqueKey,dataMap.get(uniqueKey));
                if(ids==null || ids.isEmpty()){
                    continue;
                }else{
                    //新增
                    if(!updateFlag){
                        return keys.get(uniqueKey)+"已存在";
                    }else{
                        for(Long id:ids){
                            if(!id.equals(Long.parseLong(dataMap.get("id").toString()))){
                                return keys.get(uniqueKey)+"已存在";
                            }
                        }
                    }
                }
            }
        }
        return "success";
    }


}

